package com.xbongbong.callback.help;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.callback.service.ApiCallBackService;
import com.xbongbong.callback.service.ConnectorConfigService;
import com.xbongbong.callback.service.ConnectorProducerService;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.thread.TransmittableThreadLocalUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.api.pojo.ApiCallbackSetPojo;
import com.xbongbong.pro.api.pojo.ApiWebHookRabbitMqDTO;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.rabbitmq.producer.ApiWebHookProducer;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.enums.OperateTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 *
 * @author 魏荣杰
 * @date 2019/10/10 22:05
 * @since
 * @version
 */
@Component
public class ApiCallbackHelper {
    private static final Logger LOG = LoggerFactory.getLogger(ApiCallbackHelper.class);

    @Resource
    private ApiCallBackService apiCallbackService;
    @Resource
    private ApiWebHookProducer apiWebHookProducer;
    @Resource
    private ConnectorProducerService connectorProducerService;
    @Resource
    private ConnectorConfigService connectorConfigService;

    /**
     * 单回调统一调用入库
     *  多用于业务层调用
     * @param corpid
     * @param saasMark
     * @param dataId
     * @param operateTypeEnum
     * @param xbbRefTypeEnum
     */
    @Async(value = "apiWebHookThreadPool")
    public void callback4Special(String corpid, Integer saasMark, Long dataId, OperateTypeEnum operateTypeEnum, XbbRefTypeEnum xbbRefTypeEnum) {
        LOG.info("callback4Special：" + "corpid: " + corpid  + "; saasMark: " + saasMark  + "; dataId: " + dataId + "; operateTypeEnum:" + operateTypeEnum + "; xbbRefTypeEnum: " + xbbRefTypeEnum);
        if (!hasApiCallBack(corpid, xbbRefTypeEnum) && !hasConnector(corpid, xbbRefTypeEnum)) {
            return;
        }
        SaasMarkEnum saasMarkEnum = SaasMarkEnum.getByCode(saasMark);
        callbackBatch(corpid, saasMarkEnum, Arrays.asList(dataId), operateTypeEnum, xbbRefTypeEnum);
    }

    /**
     * 批量回调统一入口
     *  多用于业务层调用
     * @param corpid
     * @param saasMarkEnum
     * @param dataIdList
     * @param operateTypeEnum
     * @param xbbRefTypeEnum
     */
    @Async(value = "apiWebHookThreadPool")
    public void callbackBatch4Special(String corpid, SaasMarkEnum saasMarkEnum, List<Long> dataIdList, OperateTypeEnum operateTypeEnum, XbbRefTypeEnum xbbRefTypeEnum) {
        LOG.info("callbackBatch4Special：" + "corpid: " + corpid + ";operateTypeEnum:" + operateTypeEnum + ";xbbRefTypeEnum: " + xbbRefTypeEnum + ";dataIdList: " + dataIdList);

        if (CollectionsUtil.isEmpty(dataIdList) || StringUtil.isEmpty(corpid)) {
            return;
        }
        if (!hasApiCallBack(corpid, xbbRefTypeEnum) && !hasConnector(corpid, xbbRefTypeEnum)) {
            return;
        }
        callbackBatch(corpid, saasMarkEnum, dataIdList, operateTypeEnum, xbbRefTypeEnum);
    }

    /**
     * 获取基础参数，并判断3秒锁，再调用底层回调方法
     * model层回调统一入口
     * @param xbbRefTypeEnum 业务类型
     * @param saasMarkEnum SaaS标记
     * @param operateTypeEnum 操作类型
     * @param param model层方法传参
     * @param isBatch 是否批量
     */
    @Async(value = "apiWebHookThreadPool")
    public void callBackByAspect(XbbRefTypeEnum xbbRefTypeEnum, SaasMarkEnum saasMarkEnum, OperateTypeEnum operateTypeEnum, Object[] param, boolean isBatch) {
        try {
            if (Objects.isNull(xbbRefTypeEnum) || Objects.isNull(saasMarkEnum) || Objects.isNull(operateTypeEnum) || Objects.isNull(param)) {
                return;
            }

            if (checkWorkFlowOperateNew(xbbRefTypeEnum, operateTypeEnum)) {
                //调用的编辑，但其实是新建审批通过
                operateTypeEnum = OperateTypeEnum.NEW;
            }

            LOG.info("callBackByAspect: " + "xbbRefTypeEnum: " + xbbRefTypeEnum +  ";saasMarkEnum: " + saasMarkEnum + ";operateTypeEnum: " + operateTypeEnum + ";isBatch: " + isBatch);

            String corpid = getCorpid(operateTypeEnum, param, isBatch);
            if (!hasApiCallBack(corpid, xbbRefTypeEnum) && !hasConnector(corpid, xbbRefTypeEnum)) {
                return;
            }
            List<Long> dataIdList = getDataIdList(param, operateTypeEnum, isBatch);
            if (CollectionsUtil.isEmpty(dataIdList) || StringUtil.isEmpty(corpid)) {
                return;
            }
            callbackBatch(corpid, saasMarkEnum, dataIdList, operateTypeEnum, xbbRefTypeEnum);
        } catch (Exception e) {
            LOG.error("callBackByAspect ERROR: xbbRefTypeEnum: " + xbbRefTypeEnum +
                    ";operateTypeEnum: " + operateTypeEnum + ";param: " + Arrays.toString(param) + ";isBatch: " + isBatch, e);
        }
    }

    /**
     * 判断是否需要将操作转成new
     * @param xbbRefTypeEnum
     * @param operateTypeEnum
     * @return
     * 创建时间 2021/11/29 5:09 PM
     * 修改时间 2021/11/29 5:09 PM
     * @author chy
     */
    private boolean checkWorkFlowOperateNew(XbbRefTypeEnum xbbRefTypeEnum, OperateTypeEnum operateTypeEnum) {
        String workFlowOperateValue = TransmittableThreadLocalUtil.getWorkFlowOperateValue();
        //使用后移除
        TransmittableThreadLocalUtil.removeWorkFlowOperateValue();
        if (Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.CUSTOMER_MANAGEMENT)) {
            xbbRefTypeEnum = XbbRefTypeEnum.CUSTOMER;
        }
        String nowValue = xbbRefTypeEnum.getCode() + "_" + "1";
        if (Objects.equals(operateTypeEnum, OperateTypeEnum.EDIT)
                && Objects.equals(workFlowOperateValue, nowValue)) {
            return true;
        }
        return false;
    }

    /**
     * 锁通过后调用
     * 批量回调
     * @param corpid
     * @param saasMarkEnum
     * @param operateTypeEnum
     * @param xbbRefTypeEnum
     */
    @Async(value = "apiWebHookThreadPool")
    public void callbackBatch(String corpid, SaasMarkEnum saasMarkEnum, List<Long> dataIdList, OperateTypeEnum operateTypeEnum, XbbRefTypeEnum xbbRefTypeEnum) {
        LOG.info("callbackBatch corpid: " + corpid + ";operateTypeEnum: " + operateTypeEnum + ";xbbRefTypeEnum: " + xbbRefTypeEnum + ";dataIdList: " + JSON.toJSONString(dataIdList));
        if (Objects.isNull(corpid) || Objects.isNull(dataIdList) || dataIdList.isEmpty() || Objects.isNull(operateTypeEnum) || Objects.isNull(xbbRefTypeEnum)) {
            return;
        }

        if (Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.CUSTOMER_MANAGEMENT)) {
            xbbRefTypeEnum = XbbRefTypeEnum.CUSTOMER;
        }
        ApiCallbackSetPojo apiCallbackSetPojo = apiCallbackService.getCorpApiCallbackUrlByType(corpid, xbbRefTypeEnum.getAlias());
        String token = apiCallbackSetPojo.getToken();
        Integer isRetry = apiCallbackSetPojo.getIsRetry();
        List<String> urls = apiCallbackSetPojo.getUrls();
        if (CollectionsUtil.isNotEmpty(urls)) {
            for (String url : urls) {
                if (StringUtil.isEmpty(url)) {
                    LOG.warn("call back url is empty! corpid:" + corpid);
                    continue;
                }
                ApiWebHookRabbitMqDTO apiWebHookRabbitMqDTO = new ApiWebHookRabbitMqDTO(corpid,
                        saasMarkEnum.getCode(), dataIdList, operateTypeEnum, url, token, isRetry, xbbRefTypeEnum);
                try {
                    apiWebHookProducer.sendMessage(apiWebHookRabbitMqDTO, xbbRefTypeEnum);
                } catch (XbbException e) {
                    LOG.error("ApiCallbackHelper.callbackBatch sendMessage error", e);
                }
            }
        }
        //连接器逻辑
        if (!TransmittableThreadLocalUtil.isConnector()) {
            triggerSendBatch(corpid, dataIdList, xbbRefTypeEnum, operateTypeEnum);
        }
    }

    /**
     * 连接器生产者消息(批量)
     * @param corpid 公司id
     * @param dataIdList 业务数据id
     * @param xbbRefTypeEnum 业务类型
     * @param operateTypeEnum 操作类型
     */
    void triggerSendBatch(String corpid, List<Long> dataIdList, XbbRefTypeEnum xbbRefTypeEnum, OperateTypeEnum operateTypeEnum) {
        LOG.info("triggerSendBatch xbbRefTypeEnum:" + xbbRefTypeEnum);
        if (!hasConnector(corpid, xbbRefTypeEnum)) {
            return;
        }
        try {
            connectorProducerService.triggerSend(corpid, xbbRefTypeEnum, operateTypeEnum.getAlias(), dataIdList);
        } catch (XbbException e) {
            LOG.error("triggerSend Exception", e);
        }
    }

    /**
     * 是否有连接器回调
     * @param corpid corpid
     * @param xbbRefTypeEnum 业务类型
     * @return
     */
    private boolean hasConnector(String corpid, XbbRefTypeEnum xbbRefTypeEnum) {
        if (Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.CONTACT)) {
            xbbRefTypeEnum = XbbRefTypeEnum.CUSTOMER;
        }
        return connectorConfigService.hasTrigger(corpid, xbbRefTypeEnum);
    }

    /**
     * 是否设置api回调
     * @param corpid
     * @param xbbRefTypeEnum
     * @return true: 设置了api回调
     */
    private boolean hasApiCallBack(String corpid, XbbRefTypeEnum xbbRefTypeEnum) {
        ApiCallbackSetPojo apiCallbackSetPojo = apiCallbackService.getCorpApiCallbackUrlByType(corpid, xbbRefTypeEnum.getAlias());
        List<String> urls = apiCallbackSetPojo.getUrls();
        return Objects.nonNull(urls) && !urls.isEmpty();
    }

    /**
     * 获取公司id
     * @param operateTypeEnum 操作类型
     * @param param 参数
     * @param isBatch 是否批量
     * @return 公司id
     */
    private String getCorpid(OperateTypeEnum operateTypeEnum, Object[] param, Boolean isBatch) {
        String corpid = null;
        switch (operateTypeEnum) {
            case NEW:
                if (isBatch) {
                    JSONArray jsonArray = JSONArray.parseArray(JSONObject.toJSONString(param[0]));
                    if (CollectionsUtil.isNotEmpty(jsonArray)) {
                        JSONObject jsonObject = jsonArray.getJSONObject(0);
                        corpid = jsonObject.getString(StringConstant.CORPID);
                        break;
                    }
                    break;
                }
                JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(param[0]));
                corpid = jsonObject.getString(StringConstant.CORPID);
                break;
            case EDIT:
                if (isBatch) {
                    corpid =  param[1].toString();
                    break;
                }
                JSONObject editObject = JSONObject.parseObject(JSONObject.toJSONString(param[0]));
                corpid = editObject.getString(StringConstant.CORPID);
                break;
            case DELETE:
                corpid =  param[1].toString();
                break;
            default:
                break;
        }
        return corpid;
    }

    /**
     * 获取dataId列表
     * @param param 参数
     * @param operateTypeEnum 操作类型
     * @param isBatch 是否批量
     * @return dataId列表
     */
    private List<Long> getDataIdList(Object[] param, OperateTypeEnum operateTypeEnum, Boolean isBatch) {
        List<Long> dataIdList = new ArrayList<>();
        switch (operateTypeEnum) {
            case EDIT:
            case NEW:
                if (isBatch) {
                    JSONArray jsonArray = JSONArray.parseArray(JSONObject.toJSONString(param[0]));
                    for (Object obj : jsonArray) {
                        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(obj));
                        Long dataId = jsonObject.getLong(BasicConstant.DATAID);
                        if (Objects.isNull(dataId)) {
                            dataId = jsonObject.getLong(BasicConstant.ID);
                        }
                        dataIdList.add(dataId);
                    }
                    break;
                }
                JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(param[0]));
                Long dataId = jsonObject.getLong(StringConstant.DATA_ID);
                if (Objects.isNull(dataId)) {
                    dataId = jsonObject.getLong(BasicConstant.ID);
                }
                dataIdList.add(dataId);
                break;
            case DELETE:
                if (isBatch) {
                    List<Long> finalDataIdList = dataIdList;
                    JSONArray jsonArray = JSONArray.parseArray(JSONObject.toJSONString(param[0]));
                    jsonArray.forEach(item -> finalDataIdList.add(Long.parseLong(item.toString())));
                    break;
                }
                dataIdList.add(Long.parseLong(param[0].toString()));
            default:
                break;
        }
        return dataIdList;
    }
}
